#include "img/SimpleImgRead.hpp"
#include "video//SimpleVideoCapture.hpp"
#include <opencv2/opencv.hpp>
#include <ostream>
#include "video/TrackVideo.hpp"
#include "TimerWheel.h"
#include "collection.h"
#include "benchmark_test.hpp"

using namespace SimpleImgHandle;
using namespace SimpleVideo;

class ImgShow {
    const char *title;
    Mat img;

public:
    ImgShow(const char *title, Mat &&img) : title(title), img(img) {}

    ImgShow(const char *title, Mat img) : title(title), img(move(img)) {}

    ImgShow() : title(nullptr) {}

public:
    const Mat &getImgInfo() const {
        return img;
    }

    const char *getTitle() const {
        return title;
    }

    void doShow(int x, int y) {
        namedWindow(title);
        moveWindow(title, x, y);
        imshow(title, img);
    }
};

class ImgShowList {
    vector<ImgShow> imgList;
public:
    ImgShowList &append(const char *title, const Mat &mat) {
        imgList.emplace_back(title, mat);
        return *this;
    }

    void showList() {
        int x = 0, y = 0;
        for (auto &item: imgList) {
            item.doShow(x, y);
            x += item.getImgInfo().cols;
        }
        waitKey();
    }
};

void test01() {
    char arr[100];
    printLn("please input fileName/fileDir :");
    cin.getline(arr, 100);
    printLn("waiting for img loading!");

    SimpleBenchMark("readTest", [&] {
        auto matList = MatList::readAllFromDirAsync(arr);
//        printLn("loading all pictures success! \n", matList);
        const auto &matVs = matList.getMatVector();
        const auto &matInfos = matList.getFileSizeInfo();
        for (int i = 0; i < matVs.size(); ++i) {
            auto imgName = std::get<0>(matInfos[i]);
            namedWindow(imgName, IMREAD_GRAYSCALE);
            resizeWindow(imgName, 1920, 1080);
            moveWindow(imgName, -1, -1);
            setWindowProperty(imgName, WINDOW_AUTOSIZE, WINDOW_AUTOSIZE);
            imshow(imgName, matVs[i]);

            waitKey(1000);
            destroyWindow(imgName);
        }
    }

    );

}


void cannyHandle1(int pos, void *data) {
    auto *tp = (tuple<vector<int>, Mat, Mat> *) data;
    int t1 = get<0>(*tp)[0] = pos;
    int t2 = get<0>(*tp)[1];
    const Mat &blur = get<1>(*tp);
    Mat &sharp = get<2>(*tp);
    Canny(blur, sharp, t1, t2, 3);
    printLn("now pos is ", get<0>(*tp));
    imshow("sharp", sharp);
}

void cannyHandle2(int pos, void *data) {
    auto *tp = (tuple<vector<int>, Mat, Mat> *) data;
    int t1 = get<0>(*tp)[0];
    int t2 = get<0>(*tp)[1] = pos;
    const Mat &blur = get<1>(*tp);
    Mat &sharp = get<2>(*tp);
    Canny(blur, sharp, t1, t2, 3);
    printLn("now pos is ", get<0>(*tp));
    imshow("sharp", sharp);
}

void cannyHandle3(int pos, void *data) {
    auto *tp = (tuple<vector<int>, Mat, Mat> *) data;
    int t1 = get<0>(*tp)[0];
    int t2 = get<0>(*tp)[1];
    if (pos < 5) {
        pos = 3;
    } else if (pos > 5 && pos < 7) {
        pos = 5;
    } else {
        pos = 7;
    }
    int s3 = get<0>(*tp)[2] = pos;
    const Mat &blur = get<1>(*tp);
    Mat &sharp = get<2>(*tp);
    Canny(blur, sharp, t1, t2, s3);
    printLn("now pos is ", get<0>(*tp));
    imshow("sharp", sharp);
}


void test02() {
    char file_path[100];
    printLn("please input fileName :");
    cin.getline(file_path, 100);
    printLn("waiting for img loading!");
    Mat src = imread(file_path);
    Mat gray, blur, sharp;
    printLn("src width: ", src.cols, " mat len: ", src.rows);
    printLn("src depth ", src.depth());
    printLn("src type ", src.type());
    printLn("src channels ", src.channels());


    cvtColor(src, gray, COLOR_BGR2GRAY);
    cv::blur(gray, blur, Size(3, 3));
    Canny(blur, sharp, 1, 9, 3);

    Mat maskImg = src.clone();
    maskImg = Scalar::all(0);

    src.copyTo(maskImg, sharp);


    vector<int> vs(3, 3);
    tuple<vector<int>, Mat, Mat> data = make_tuple(vs, blur, sharp);

    namedWindow("sharp");
    cv::createTrackbar("threadHold1", "sharp", &(vs[0]), 1000, [](int pos, void *data) {
        auto *tp = (tuple<vector<int>, Mat, Mat> *) data;
        int t1 = get<0>(*tp)[0] = pos;
        int t2 = get<0>(*tp)[1];
        const Mat &blur = get<1>(*tp);
        Mat &sharp = get<2>(*tp);
        Canny(blur, sharp, t1, t2, 3);
        printLn("now pos is ", get<0>(*tp));
        imshow("sharp", sharp);
    }, &data);
    cv::createTrackbar("threadHold2", "sharp", &(vs[1]), 1000, cannyHandle2, &data);
    cv::createTrackbar("minDealUnit", "sharp", &(vs[2]), 7, cannyHandle3, &data);
    imshow("src", src);

    waitKey();
//    ImgShowList showList;
//    showList
//            .append("src", src)
//            .append("gray", gray)
//            .append("blur", blur)
//            .append("sharp", sharp)
//            .append("mask", maskImg);
//    showList.showList();



}


bool contour(Mat image) {
    Mat img_gau, img_open, img_seg, img_edge;
    // 高斯模糊
    GaussianBlur(image, img_gau, Size(7, 7), 0, 0);
    //imshow("绘制方框", image);
//    开运算
    Mat element = getStructuringElement(MORPH_RECT, Size(image.rows, image.cols));
    morphologyEx(img_gau, img_open, MORPH_OPEN, element);
    addWeighted(img_gau, 1, img_open, -1, 0, img_open);
    // 阈值分割
    //threshold(img_open, img_seg, 20, 255, THRESH_BINARY_INV);
    adaptiveThreshold(img_open, img_seg, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 21, 10);
    imshow("1", img_seg);
    int row_dst = img_seg.rows;
    int col_dst = img_seg.cols;
    int mid_sp = 0, mid_cz = 0;
    vector<int> mid_sph, mid_czh;
    for (int i = 0; i < row_dst; i++) {
        for (int j = 0; j < col_dst; j++) {
            mid_sp = mid_sp + img_seg.at<uchar>(i, j);
        }

        cout << "第" << i + 1 << "行的水平投影为：" << mid_sp << endl;//144000
        mid_sph.push_back(mid_sp);
        mid_sp = 0;
    }
    int sp1 = 0, sp2 = 0;
    for (int i = 0; i < mid_sph.size(); i++) {
        if (mid_sph[i] != 0 && sp1 == 0) {
            sp1 = i;
        } else if (mid_sph[i] == 0 && sp1 != 0) {
            sp2 = i;
            break;
        }
    }
    int spc;
    spc = abs(sp1 - sp2);
    Mat new_sp_img = Mat::zeros(Size(500, spc), CV_8UC1);
    for (int i = 0; i < new_sp_img.rows; i++) {
        for (int j = 0; j < new_sp_img.cols; j++) {
            new_sp_img.at<uchar>(i, j) = img_seg.at<uchar>(i + sp1, j);
        }
    }
    imshow("ll", new_sp_img);
    for (int i = 0; i < col_dst; i++) {
        for (int j = 0; j < row_dst; j++) {
            mid_cz = mid_cz + img_seg.at<uchar>(j, i);
        }

        cout << "第" << i + 1 << "列的垂直投影为：" << mid_cz << endl;//144000
        mid_czh.push_back(mid_cz);
        mid_cz = 0;
    }

    vector<Vec2i> czd;
    int cz1 = 0, cz2 = 0;
    for (int i = 0; i < mid_czh.size(); i++) {
        if (mid_czh[i] != 0 && cz1 == 0) {
            cz1 = i;
        } else if (mid_czh[i] == 0 && cz1 != 0) {
            cz2 = i;
            czd.push_back(Vec2i(cz1, cz2));
            cz1 = 0;
        }
    }
    Mat new_cz_img = Mat::zeros(Size(spc, spc), CV_8UC1);
    int czd_zs = 0;
    //new_sp_img
    for (int i = 0; i < czd.size(); i++) {
        int sub_cz = (spc - abs(czd[i][0] - czd[i][1])) / 2;
        for (int j = 0; j < spc; j++) {
            czd_zs = czd[i][0];
            for (int k = 0; k < spc; k++) {
                if (k < sub_cz) {
                    new_cz_img.at<uchar>(j, k) = 0;
                } else if (czd_zs <= czd[i][1]) {
                    new_cz_img.at<uchar>(j, k) = new_sp_img.at<uchar>(j, czd_zs++);
                } else {
                    new_cz_img.at<uchar>(j, k) = 0;
                }
            }
        }
        Mat final_cc;
        resize(new_cz_img, final_cc, Size(28, 28));
        imwrite("number" + to_string(i) + ".jpg", final_cc);
        imshow("number" + to_string(i), final_cc);
    }
    waitKey(0);
    return true;
}


void gotoxy(int x, int y) {                    //移动光标
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {static_cast<SHORT>(x), static_cast<SHORT>(y)});
}

static mutex mut;

template<typename T>
void printStartWith(T &&arg, int x, int y) {
    lock_guard<mutex> ol{mut};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), {static_cast<SHORT>(x), static_cast<SHORT>(y)});
    std::cout << arg << std::endl;
}

/*求方程在x处斜率*/
template<typename Fn>
double getRatio(Fn &&fn, double x, double mis = 1e-6) {
    return (fn(x + mis) - fn(x - mis)) / (2 * mis);
}

double getSqrt(double y, double mis = 1e-6) {
    auto fn = [](double x) {
        return x * x;
    };
    double rs = y;
    double last = 0;
    while (abs(rs - last) > mis) {
        last = rs;
        rs = (fn(rs) + y) / getRatio(fn, rs);
    }
    return rs;
}

template<typename Fn>
double getXByFunc(Fn &&fn, double y, double mis = 1e-6) {
    double rs = y, last;
    do {
        last = rs;
        rs = (fn(rs) + y) / abs(getRatio(fn, rs));
    } while (abs(rs - last) > mis);
    return rs;
}


float InvSqrt(float x) {
    float xhalf = 0.5f * x;
    long i = *(long *) &x; // get bits for floating VALUE
    i = 0x5f3759df - (i >> 1); // gives initial guess y0
    x = *(float *) &i; // convert bits BACK to float
    x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
//    x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
//    x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy

    return 1 / x;

    std::atomic_int a;
}

//BENCHMARK_MAIN()


//int main() {
////    using namespace TrackVideo;
//    using namespace SimpleImgHandle;
//    using namespace cv;
//    using std::string;
//    typedef TimerWheelUtil::TimerWheel::TimePos TP;
//
//
//
//
////    auto tp11 = TP::ms2TimePos(1, 999);
////    TP test1{100, 0, 0, 0, 1};
////    TP test2{1, 0, 0, 0, 2};
////    printLn(test1);
////    printLn(test2);
////    printLn(test2 = test1);
////    printLn(TP{test1});
//
////    auto tmpp = [](const int &i) {
////        return &const_cast<int &>(i);
////    };
////    int i = 10;
////    int *pi = tmpp(1000);
////    printLn(i);
////    printLn(*pi);
//
//
////    typedef std::list<C> lc;
////
////    lc *lcp = new lc[10];
////    printLn(lcp[0]);
////    delete[] lcp;
////    printLn(lcp[0].empty());
//
////    TimerWheelUtil::TimerWheel timerWheel;
////    timerWheel.initTimerWheel(100, TimerWheelUtil::HOUR_MIN_SECOND_MS_UNIT);
////    std::atomic_int a{0};
////    auto tid = timerWheel.addTimer(500, make_tuple([](atomic_int &i) {
////        printLn(++i);
////    }, ref(a)));
////    std::this_thread::sleep_until(std::chrono::steady_clock::now() + std::chrono::milliseconds(1000 * 1));
////    timerWheel.deleteTimer(tid);
//
//    //    MultiBenchMarkAsync({
////                            {
////                                "+=", [&tp10, &tp2, ind] {
////                                    for (int i = 0; i < ind; ++i) {
////                                        tp10 += tp2;
////                                    }
////                                }
////                            },
////                            {
////                                "Add", [&tp11, &tp2, ind] {
////                                    for (int i = 0; i < ind; ++i) {
////                                        tp11.Add(tp2);
////                                    }
////                                }
////                            }
////                        });
//
//
////    Mat mat = imread(R"(C:\Users\admin\Desktop\chima.jpg)", 0);
////    cv::imshow("mat", mat);
////
////    waitKey();
////    Mat mat = imread(R"(C:\Users\admin\Desktop\chima.jpg)");
////    int cv = 0, sv = 0;
////    int exRate = 1;
////    int selRate = 1;
////
////    auto tpc = make_tuple([&cv, &sv, &mat](int pos) {
////        cout << "cv,sv :" << cv << "," << sv << endl;
////        Mat dst;
////        bilateralFilter(mat, dst, 0, cv = pos, sv);
////        imshow("mat", dst);
////    });
////
////    auto tps = make_tuple([&cv, &sv, &mat](int pos) {
////        cout << "cv,sv :" << cv << "," << sv << endl;
////        Mat dst;
////        bilateralFilter(mat, dst, 0, cv, sv = pos);
////        imshow("mat", dst);
////    });
////
////    auto exRateTp = make_tuple([&exRate](int pos) {
////        exRate = pos;
////    });
////
////    auto selRateTp = make_tuple([&selRate](int pos) {
////        selRate = pos;
////    });
////
////
////    Rect totalRect(0, 0, mat.cols, mat.rows);
////
////    auto mp = make_tuple([&totalRect, &mat, &exRate, &selRate](int event, int x, int y, int flag) {
////        switch (event) {
////            case EVENT_MOUSEMOVE:
////                //显示图像像素值
////                if (static_cast<int>(mat.channels()) == 1) {
////                    //若图像为单通道图像，则显示鼠标点击的坐标以及灰度值
////                    switch (mat.type()) {
////                        case 0:
////                            cout << "at (" << x << ", " << y << " ) value is: "
////                                 << static_cast<int>(mat.at<uchar>(Point(x, y))) << endl;
////                            break;
////                        case 1:
////                            cout << "at (" << x << ", " << y << " ) value is: "
////                                 << static_cast<int>(mat.at<char>(Point(x, y))) << endl;
////                            break;
////                        case 2:
////                            cout << "at (" << x << ", " << y << " ) value is: "
////                                 << static_cast<int>(mat.at<ushort>(Point(x, y))) << endl;
////                            break;
////                        case 3:
////                            cout << "at (" << x << ", " << y << " ) value is: "
////                                 << static_cast<int>(mat.at<short>(Point(x, y))) << endl;
////                            break;
////                        case 4:
////                            cout << "at (" << x << ", " << y << " ) value is: "
////                                 << static_cast<int>(mat.at<int>(Point(x, y))) << endl;
////                            break;
////                        case 5:
////                            cout << "at (" << x << ", " << y << " ) value is: "
////                                 << static_cast<int>(mat.at<float>(Point(x, y))) << endl;
////                            break;
////                        case 6:
////                            cout << "at (" << x << ", " << y << " ) value is: "
////                                 << static_cast<int>(mat.at<double>(Point(x, y))) << endl;
////                            break;
////                    }
////                } else {
////                    //若图像为彩色图像，则显示鼠标点击坐标以及对应的B, G, R值
////                    auto scalar = RScalar(static_cast<int>(mat.at<Vec3b>(Point(x, y))[2]),
////                                          static_cast<int>(mat.at<Vec3b>(Point(x, y))[1]),
////                                          static_cast<int>(mat.at<Vec3b>(Point(x, y))[0]));
////                    int selectBase = 5 * (selRate == 0 ? 1 : selRate);
////                    int expandBase = 1 * (exRate == 0 ? 1 : exRate);
////                    Mat expand;
////                    Rect aimRect();
////                    cv::resize(mat(Rect(x - selectBase, y - selectBase, 2 * selectBase, 2 * selectBase) & totalRect),
////                               expand, Size(2 * selectBase * expandBase, 2 * selectBase * expandBase));
////                    cv::imshow("expand", expand.clone());
////                    Mat tmpMap = mat.clone();
////                    rectangle(tmpMap, Rect(x - selectBase, y - selectBase, 2 * selectBase, 2 * selectBase) & totalRect,
////                              Scalar(0, 0, 255));
////                    cv::imshow("mat", tmpMap);
////                    cout << "at (" << x << ", " << y << ")"
////                         << "R G B value is " << scalar
////                         << endl;
////                }
////
////                break;
////        }
////    });
////
////    MatShow("mat", mat).withWindows()
////            .regTarBar("color value", 1000, &tpc)
////            .regTarBar("space value", 100, &tps)
////            .regTarBar("expand value", 20, &exRateTp)
////            .regTarBar("select value", 100, &selRateTp)
////            .regMouseAction(&mp)
////            .show();





//    const char *input = InputHelper::getInput("please input your choice! 1 head camera 2.video file path");
//    if (string(input) == "1") {
//        TrackVideo::testTrack1(nullptr);
//    } else {
//        const char *input2 = InputHelper::getInput("please input your video file path!");
//        TrackVideo::testTrack1(input2);
//    }

//}
BENCHMARK_REGISTER(test, [] {
    for (int i = 0; i < 1000; ++i) {

    }
})->Arg(10);
BENCHMARK_MAIN();